/*
		ServEngine framework library
		A library to fast develop JEE online applications
	
		Copyright 2008 José Ignacio de Córdoba Álvaro

		Licensed under the Apache License, Version 2.0 (the "License");
		you may not use this file except in compliance with the License.
		You may obtain a copy of the License at

				http://www.apache.org/licenses/LICENSE-2.0

		Unless required by applicable law or agreed to in writing, software
		distributed under the License is distributed on an "AS IS" BASIS,
		WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
		See the License for the specific language governing permissions and
		limitations under the License.
	
		Jose Ignacio de Cordoba Alvaro
		http://ignacio.decordoba.com
*/

package com.servengine.struts;

import com.servengine.image.TextImage;
import com.servengine.image.ThumbnailUtils;
import com.servengine.log.LogManagerLocal;
import com.servengine.portal.*;
import com.servengine.user.*;

import com.servengine.util.File;
import com.servengine.util.MailRelay;
import com.servengine.util.ServletContextUtils;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import java.io.InputStream;
import java.io.OutputStream;

import java.io.Writer;

import java.util.Calendar;
import java.util.GregorianCalendar;

import java.util.Random;

import java.util.ResourceBundle;

import javax.naming.*;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

import javax.servlet.jsp.jstl.core.Config;

import javax.transaction.UserTransaction;
import javax.transaction.Status;

import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.actions.ActionDispatcher;
import org.apache.struts.upload.FormFile;

/**
 * All actions in ServEngine extend this abstract class
 */
public abstract class ClientAction extends Action
{
	private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog( ClientAction.class.getName() );

	protected ActionDispatcher dispatcher = new ActionDispatcher(this,ActionDispatcher.DEFAULT_FLAVOR);

//	protected javax.naming.InitialContext initialcontext;

	protected ServletContext servletcontext;

//	protected StatelessUtilsLocal statelessutils;
	protected UserManagerLocal usermanager;
	protected PortalManagerLocal portalmanager;
//	protected PropertyManager propertyManager;
	protected com.servengine.log.LogManagerLocal logManager;
	protected ResourceBundle resourceBundle;
	
	/**
	 * This method is overriden to use dispatcher to group several methods in the same Action class.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		return dispatcher.execute(mapping, form, request, response);
	}

	public void setServlet(ActionServlet servlet)
	{
		super.setServlet(servlet);
		servletcontext = servlet.getServletContext();
/*	  String url=null;
	  try
	  {
	    Properties properties = new Properties();
			try
			{
				properties.load(getClass().getClassLoader().getResourceAsStream("jbossclienturls.properties"));
			} catch (NullPointerException e)
			{
				log.warn("ClassLoader can't find jbossclienturls.properties");
			}
			if (properties!=null)
	    {
	      defaultUrl = properties.getProperty("default");
	      if (defaultUrl==null)
	      {
	        defaultUrl="jnp://localhost:1099/"+servletcontext.getServletContextName();
	        log.warn("No default url found. Defaults to localhost JBoss url with ServletContextName: "+servletcontext.getServletContextName());
	        log.warn("defaultUrl="+defaultUrl);
	      }
	      url = properties.getProperty(getClass().getName());
	      if (url==null)
	        url = defaultUrl;
	    }
	  } catch (Throwable e)
	  {
	    log.error(e.getMessage());
	    url="jnp://localhost:1099/"+servletcontext.getServletContextName();
	    log.warn("Using default url:"+url);
	  }*/

//		url = servletcontext.getServletContextName();
//	  if (log.isDebugEnabled()) log.debug(getClass().getName()+":url="+url);

//	  java.util.Hashtable<String,String> contextProps = new java.util.Hashtable<String,String>();
//	  contextProps.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
//	  contextProps.put("java.naming.provider.url", url);
//	  contextProps.put(javax.naming.Context.URL_PKG_PREFIXES,"org.jboss.naming:org.jnp.interfaces");
	  try
	  {
//    java.util.Hashtable<String,String> contextProps = new java.util.Hashtable<String,String>();
 //   contextProps.put("java.naming.provider.url", "java:comp/env/"+servletcontext.getServletContextName());

			Context context = (Context)new javax.naming.InitialContext().lookup(servletcontext.getServletContextName());
	    usermanager = (UserManagerLocal)context.lookup("UserManager/local"); // Las cogemos del default.
	    portalmanager = (PortalManagerLocal)context.lookup("PortalManager/local"); // Las cogemos del default.
			try
			{
				logManager = (LogManagerLocal)context.lookup("LogManager/local");
			} catch (Throwable e)
			{
			  if (log.isDebugEnabled())
			    log.debug("LogManager not found: ",e);
			  else
			    log.warn("LogManager not found:"+e.getMessage());
			}
//			contextProps.remove("java.naming.provider.url"); //FIXME Expurganda (si no es necesario)
//	    contextProps.put("java.naming.provider.url", url);
//	    initAction(new javax.naming.InitialContext(contextProps));
	    initAction(context);
		} catch (Throwable e)
	  {
	    throw new IllegalStateException(e.getMessage(),e);
	  }
		
		Class myClass = getClass();
	  while (resourceBundle==null && myClass!=null)
			try
			{
				resourceBundle = ResourceBundle.getBundle(myClass.getPackage().getName()+".i18n");
			} catch (java.util.MissingResourceException e)
			{
				myClass=myClass.getSuperclass();
				if (myClass==null)
					log.warn("No resource bundle for "+getClass());
			}
		
	  log.info(getClass().getName()+".setServlet() initialized");
	}

	/**
	 * System invokes this method after Action initialization in case class needs any resources.
	 * @param initialcontext
	 */
	abstract protected void initAction(javax.naming.Context initialcontext);
	
	/**
	 * @deprecated Please use getServletContextUtils()
	 * 
	 * @return
	 */
	public com.servengine.util.ServletContextUtils getServletcontextutils()
	{
		return (com.servengine.util.ServletContextUtils)servletcontext.getAttribute(ServletContextUtils.SERVLETCONTEXT_ATTRIBUTE_NAME);
	}
	public com.servengine.util.ServletContextUtils getServletContextUtils()
	{
	  return (com.servengine.util.ServletContextUtils)servletcontext.getAttribute(ServletContextUtils.SERVLETCONTEXT_ATTRIBUTE_NAME);
	}

/*	public ServletContextMailRelay getServletContextMailRelay()
	{
		ServletContextMailRelay mailRelay = (ServletContextMailRelay)servletcontext.getAttribute(ServletContextMailRelay.SERVLETCONTEXT_ATTRIBUTE_NAME);
		if (mailRelay==null)
			throw new IllegalStateException("No "+ServletContextMailRelay.SERVLETCONTEXT_ATTRIBUTE_NAME+" object in ServletContext scope.");
		return mailRelay;
	}*/

	public MailRelay getMailRelay()
	{
		return new MailRelay(servletcontext);
	}


	/**
	 * Returns "index" maping forward. This method can be overriden to implementa default unespecificed method behaviour for any Action"s" object.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	public ActionForward index(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		return mapping.findForward("index");
	}

	/**
	 * default unspecified invocation calls index() method.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
	  return index(mapping,form,request,response);
	}

	/**
	 * default create action just returns success forward.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	public ActionForward create(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
	  return mapping.findForward("success");
	}
	/**
	 * default home invocation calls index() method. Method to be overriden if special processing is needed for HOME info of this component.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	public ActionForward home(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		return unspecified(mapping,form,request,response);
	}
	/**
	 * @deprecated This things should be done with an ActionForm or DynaActionForm
	 * Convenience method to get a Date given 1, 2 or 3 parameters. Prefix used to be able to insert several dates in the same request.
	 * @param request
	 * @param pfx
	 * @return
	 */
	protected java.util.Date getDatefromdaymonthyear(HttpServletRequest request, String pfx)
	{
	  if ((request.getParameter(pfx+"day")!=null && request.getParameter(pfx+"day").length()>0) ||
			(request.getParameter(pfx+"month")!=null && request.getParameter(pfx+"month").length()>0) ||
			(request.getParameter(pfx+"year")!=null && request.getParameter(pfx+"year").length()>0))
	  {
	    Calendar calendar = GregorianCalendar.getInstance();
	    if (request.getParameter(pfx+"year")!=null && request.getParameter(pfx+"year").length()>0)
	      calendar.set(Calendar.YEAR,Integer.parseInt(request.getParameter(pfx+"year")));
	    if (request.getParameter(pfx+"month")!=null && request.getParameter(pfx+"month").length()>0)
	      calendar.set(Calendar.MONTH,Integer.parseInt(request.getParameter(pfx+"month"))-1);
	    if (request.getParameter(pfx+"day")!=null && request.getParameter(pfx+"day").length()>0)
	      calendar.set(Calendar.DAY_OF_MONTH,Integer.parseInt(request.getParameter(pfx+"day")));
			calendar.set(Calendar.HOUR,0);
			calendar.set(Calendar.MINUTE,0);
			calendar.set(Calendar.SECOND,0);
			calendar.set(Calendar.MILLISECOND,0);
			return calendar.getTime();
		}
		return null;
	}
	/**
	 * Convenience method to browser any search action. It just returns "search" forward without any processing.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	public ActionForward browseSearch(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
	  return mapping.findForward(request.getParameter("forward")==null || request.getParameter("forward").length()==0?"search":request.getParameter("forward"));
	}

	public ActionForward search(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
	  return mapping.findForward("unimplemented");
	}


	public ActionForward forward(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		return mapping.findForward(request.getParameter("forwardName"));
	}

	public ActionForward page(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		return mapping.findForward("page"+request.getParameter("page"));
	}

	/**
	 * Convenience method to populate ActionForm in a wizard "several steps" action. It just returns "success" forward without any processing.
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	public ActionForward wizard(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		return mapping.findForward(request.getParameter("forward")==null?"success":request.getParameter("forward"));
	}

	private File getServiceMaindir(String serviceid, boolean visible)
	{
		String dirpath = servletcontext.getInitParameter(serviceid+".workdir");
		if (dirpath==null)
		{
			dirpath=servletcontext.getRealPath(visible?"/servicefiles/"+serviceid:"/WEB-INF/servengine/"+serviceid);
			if (log.isDebugEnabled()) log.debug("No "+serviceid+".workdir specified in web.xml. Defaults to "+dirpath);
		}
		return new File(dirpath);
	}
/*	private File getServiceMaindir(boolean visible)
	{
		return getServiceMaindir(getClass().getPackage().getName().split("\\.")[2],visible);
	}*/

	protected File getServicedir(String portalid, String serviceid)
	{
		return getServicedir(portalid,serviceid,true);
	}

	protected File getServicedir(String portalid, String serviceid, boolean visible)
	{
		log.warn("Invocation of deprecated getServicedir()");
		File dir = new File(getServiceMaindir(serviceid,visible),portalid);
		dir.mkdirs();
		return dir;
	}

	protected File getServicedir(String portalid, boolean visible)
	{
		return getServicedir(portalid,getClass().getPackage().getName().split("\\.")[2],visible);
	}

	protected boolean hasFileInServiceDir(Portal portal, Integer id, boolean visible)
	{
		return new File(getServicedir(portal.getId(),visible),""+id).exists();
	}

	/**
	 * @deprecated lease use getUserSession(request)
	 * @param request
	 * @return
	 */
	protected UserActionForm getUser(HttpServletRequest request)
	{
		UserActionForm user = new UserActionForm();
		try
		{
			org.apache.commons.beanutils.PropertyUtils.copyProperties(user,getUserSession(request).getUser());
		} catch (Exception e)
		{
			throw new IllegalStateException(e);
		}
		return user;
	}
 protected UserSessionSBean getUserSession(HttpServletRequest request)
 {
	 return (UserSessionSBean)request.getSession(true).getAttribute(AuthorizeAction.USERSESSIONSBEAN_ATTRIBUTEID);
 }
 /**
	* Tries to guess portalid from cookies information
	* @param request
	* @return
	*/
 protected String getPortalidfromcookies(HttpServletRequest request)
 {
	 Cookie[] cookies = request.getCookies();
	 if (cookies != null)
		 for (int i = 0; i < cookies.length; i++)
			 if ("portalid".equals(cookies[i].getName()))
				 return cookies[i].getValue();
	 throw new UnidentifiedPortalException();
 }
 protected ActionErrors getErrorsFromException(Throwable e)
 {
	 ActionErrors errors = new ActionErrors();
		while (e.getCause()!=null)
			e=e.getCause();
		ActionMessage message = null;
		if (e.getMessage()==null || e.getMessage().length()==0 || e.getMessage().indexOf(" ")>-1)
			message = new ActionMessage(e.getClass().getName(),e.getMessage());//La clave es el nombre de la excepción con el mensaje como argumento
		else
		  message = new ActionMessage(e.getMessage());//La clave es el mensaje en la excepción
		errors.add("error",message);
		return errors;
 }
 protected UserTransaction getUserTransaction()
 {
	 try
	 {
	   Object obj = new InitialContext().lookup("java:comp/UserTransaction");
		 if (!(obj instanceof javax.transaction.UserTransaction))
		 {
			 log.error(obj.getClass()+" is not instance of javax.transaction.UserTransaction, but "+obj.getClass().getInterfaces()[0]+", returning null.");
			 return null;
		 }
	   javax.transaction.UserTransaction tx = (javax.transaction.UserTransaction)obj;
	   //com.arjuna.ats.jta.UserTransaction.userTransaction();
	   if (log.isDebugEnabled())
	    log.debug("Transaction status: "+tx.getStatus()+" ("+obj+")");
	   if (tx.getStatus()==Status.STATUS_NO_TRANSACTION)
			 try
			 {
					tx.begin();
			 } catch (Throwable e)
			 {
				 log.error(e.getMessage(),e);
			 }
			else if (tx.getStatus()==Status.STATUS_MARKED_ROLLBACK)
			{
				log.warn("Rolling back STATUS_MARKED_ROLLBACK transaction "+tx);
				tx.rollback();
			  log.warn("Reinvoking getUserTransaction() method");
				return getUserTransaction();
			} else
				log.warn("Returning transaction with status "+tx.getStatus()+" ("+tx+")");
		return tx;
	 } catch (Exception e)
	 {
		 throw new IllegalStateException(e.getMessage(),e);
	 }
 }

	/**
	 * @deprecated manual check must be used as this exception cannot be thrown here to be redirected to loginform.
	 * @param request
	 */
	protected void checkAuthUser(HttpServletRequest request)
	{
		if (getUserSession(request).isGuest())
		{// El siguiente código está pasado a com.servengine.struts.ExceptionHandler
			//if (request.getMethod().toUpperCase().equals("GET"))
			//	request.getSession().setAttribute("requestURI",request.getRequestURI()+(request.getQueryString()==null?""+"?"+request.getQueryString()));
			throw new UnidentifiedUserException();
		}
	}

	protected void setLocale(javax.servlet.http.HttpServletRequest request, java.util.Locale locale)
	{
	  request.getSession().setAttribute(Config.FMT_LOCALE, locale); //También para JSTL, no solo para Struts.
	  request.getSession().setAttribute("javax.servlet.jsp.jstl.fmt.locale.session", locale);
		super.setLocale(request,locale);
	}

	public ActionForward setLocale(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		String locale = request.getParameter("locale");
		javax.servlet.jsp.jstl.core.Config.set(request.getSession(), javax.servlet.jsp.jstl.core.Config.FMT_LOCALE, locale);
	  setLocale(request,new java.util.Locale(locale));
		Cookie cookie = new Cookie("locale",locale);
		cookie.setPath("/");
		cookie.setMaxAge(365*12*30*24*60*60);
		cookie.setComment("¡Viva España!");
		response.addCookie(cookie);
		if (request.getHeader("referer")==null || request.getHeader("referer").toLowerCase().contains("locale"))
			response.sendRedirect(getUserSession(request).getPortal().getHomeurl());
		else
			response.sendRedirect(request.getHeader("referer"));
		return null;
	}

	protected boolean validateRandomText(HttpSession session, String value)
	{
		if (value==null)
			return false;
		String text = (String)session.getAttribute("randomValidationText");
		session.removeAttribute("randomValidationText");
		return value.equals(text);
	}
	public ActionForward randomValidationTextImage(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException
	{
		String text = new String();
		for (int i=0; i<5; i++)
			text+=(char)(new Random().nextInt(25)+65);
		request.getSession().setAttribute("randomValidationText",text);
		response.setContentType("image/png");
		new TextImage(150,75,text).writeAsPng(response.getOutputStream());
		return null;
	}
	protected void write(InputStream fis, HttpServletResponse response) throws IOException
	{
		OutputStream fos = response.getOutputStream();
		byte[] buf = new byte[1024];
		int a = 0;
		while((a=fis.read(buf))!=-1)
			fos.write(buf, 0, a);
		fis.close();
	}
	protected void write(BufferedReader fis, HttpServletResponse response) throws IOException
	{
		response.setCharacterEncoding("utf-8");
		Writer fos = response.getWriter();
		String buf = null;
		while((buf=fis.readLine())!=null)
			fos.write(buf+"\n");
		fis.close();
	}
	public void write(byte[] data, HttpServletResponse response) throws IOException
	{
		write(new ByteArrayInputStream(data),response);
	}
	
	public String getRequestURLDir(HttpServletRequest request)
	{
		String requestURL = request.getRequestURL().toString();
		return requestURL.substring(0,requestURL.lastIndexOf(("/")));
	}

	protected byte[] getImage(FormFile formFile, int imageWidth, int imageHeight, int maxFileSize) throws IOException
	{
	  Image image=ThumbnailUtils.JPEGtoImage(formFile.getInputStream());
	  if (formFile.getFileSize()>maxFileSize||image.getWidth(null)>imageWidth||image.getHeight(null)>imageHeight)
	  {
	    BufferedImage scaledimage=ThumbnailUtils.getScaledimage(image, new Dimension(imageWidth,imageHeight));
	    ByteArrayOutputStream byteOutputStream=new ByteArrayOutputStream();
	    ThumbnailUtils.writeJPG(scaledimage, byteOutputStream, (float)0.5);
	    return byteOutputStream.toByteArray();
	  } else
	    return formFile.getFileData();
	}

	public byte[] getImage(ImageFormFileForm form, int maxFileSize) throws IOException
	{
		return getImage(form.getImageFormFile(),form.getImageWidth(), form.getImageHeight(), maxFileSize);
	}

	public void storeImage(Portal portal, FormFile formFile, Integer id, int imageWidth, int imageHeight, float imageQuality, int maxFileSize) throws IOException
	{
	  File file = new File(getServicedir(portal.getId(),true),""+id);
	  Image image = ThumbnailUtils.JPEGtoImage(formFile.getInputStream());
	  if (formFile.getFileSize()>maxFileSize||image.getWidth(null)>imageWidth||image.getHeight(null)>imageHeight)
	  {
	    BufferedImage scaledimage = ThumbnailUtils.getScaledimage(image,new Dimension(imageWidth,imageHeight));
	    ThumbnailUtils.writeJPG(scaledimage,new java.io.FileOutputStream(file),imageQuality>0?imageQuality:1);
	  } else
	    file.store(formFile.getInputStream());
	}

	public void storeImage(Portal portal, ImageFormFileForm form, Integer id, int maxFileSize) throws IOException
	{
	  storeImage(portal,form.getImageFormFile(), id, form.getImageWidth(), form.getImageHeight(), form.getImageQuality(), maxFileSize);
		form.setHasImage(true);
	}
	public boolean deleteServiceFile(Portal portal, Integer id)
	{
		return new File(getServicedir(portal.getId(),true),""+id).delete();
	}
}
